#region Copyright & License
/* Tyng.Data
 * Copyright (c) 2007, Paul Tyng, All rights reserved.
 * Code licensed under the BSD License:
 * http://www.opensource.org/licenses/bsd-license.php
 * Downloaded from: http://code.google.com/p/tyngdata/
*/
#endregion

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;

namespace Tyng.Data
{
    /// <summary>
    /// FieldConverter for byte[] data.
    /// </summary>
    /// <remarks>
    /// This converter takes hex data in a text file and converts it to a byte[] of data in .NET.
    /// </remarks>
    [Serializable]
    public sealed class HexFieldConverter : IFieldConverter
    {
        private HexFieldConverter()
        {
        }

        #region IFieldConverter Members

        object IFieldConverter.ConvertFromString(Type toType, string data)
        {
            if (data.Length == 0 || data == "0x") return new byte[0];

            if (data.Length > 2 && data.StartsWith("0x"))
                data = data.Substring(2);

            //prepend a 0 if length is off by 1 (ie. F = 0F = 15)
            if (data.Length % 2 == 1)
                data = '0' + data;

            int byteLength = data.Length / 2;
            byte[] bytes = new byte[byteLength];
            string hex;
            int j = 0;
            for (int i = 0; i < bytes.Length; i++)
            {
                hex = new String(new Char[] { data[j], data[j + 1] });
                bytes[i] = byte.Parse(hex, NumberStyles.HexNumber);
                j = j + 2;
            }

            if(toType == typeof(byte[]))
                return bytes;
            
            if(toType == typeof(byte))
            {
                if (bytes.Length != 1) throw new OverflowException();
                    return bytes[0];
            }

            if(toType == typeof(sbyte))
            {
                if (bytes.Length != 1) throw new OverflowException();
                    return (sbyte)bytes[0];
            }

            if(toType == typeof(bool))
            {
                if (bytes.Length != 1) throw new OverflowException();
                    return bytes[0] != 0;
            }

            if(toType == typeof(char))
            {
                if (bytes.Length != 1) throw new OverflowException();
                    return (char)bytes[0];
            }

            if(toType == typeof(short)) return (short)GetLong(2, bytes);
            if(toType == typeof(int)) return (int)GetLong(4, bytes);
            if(toType == typeof(long)) return GetLong(8, bytes);
                    
            throw new InvalidCastException();
        }

        #endregion

        private static long GetLong(int numberOfBytes, byte[] data)
        {
            if (data.Length > numberOfBytes) throw new OverflowException("numberOfBytes");

            long result = 0;

            for (int i = 0; i < data.Length; i++)
            {
                long b = data[i];
                b <<= (data.Length - i - 1) * 8;
                result += b;
            }

            return result;
        }

        private static HexFieldConverter _default;

        /// <summary>
        /// The default instance of the converter.
        /// </summary>
        public static HexFieldConverter Default
        {
            get
            {
                if (_default == null)
                    _default = new HexFieldConverter();

                return _default;
            }
        }
    }
}
